// @(#)server.cc
// Time-stamp: <2009-02-11 15:07:22 root>
// Copyright 2008 Julian Qian
// Author: root@localhost.localdomain
// Version: $Id: process.cc,v 0.0 2008/10/07 14:12:19 root Exp $
// Keywords: 
//

#include "server.hh"
#include "heart.hh"
#include "log.hh"
#include "inifile.hh"
#include "proto.hh"
#include "excep.hh"
#include "alarm.hh"


#include <errno.h>
#include <string.h>

using namespace std;


// Server* Server::_theServer = NULL;

// Server*
// Server::theServer(){
//     if(NULL == _theServer){
//         _theServer = new Server();
//     }
//     return _theServer;
// }

Server::Server(){
	if(-1 == (_sock = socket(AF_INET, SOCK_DGRAM, 0))){
        throw SocketException("Create Socket");
    }
    
    // memset(_buffer, 0, PROTO_LENG);
}

Server::~Server(){
	Close();
}

char*
Server::print_hex(char *hex, int len){
	int i, sl;
    memset(tmpbuf, 0, BUFFER_LEN);
    
	for (i=0; i<len; i++) {
        sl = strlen(tmpbuf);
		snprintf(tmpbuf + sl, BUFFER_LEN - sl - 1, "%#x ",hex[i]);
	}
    return tmpbuf;
}

void
Server::Listen(int port){
	memset(&_addr, 0, sizeof(_addr));
	_addr.sin_family = AF_INET;
	_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	_addr.sin_port = htons((uint16_t)port);

	// bind the socket
	if(-1 == bind(_sock, (SA *)&_addr, sizeof(_addr))){
        throw SocketException("The port has been used");
    }

    LInfo("Listen at " << host_addr() << ":" << port << " now ...");

	int opt_num = 240*1024;
	setsockopt(_sock, SOL_SOCKET, SO_RCVBUF, &opt_num, sizeof(opt_num));
}

void
Server::sock_timeout(int delay = 3){
    // Set exipred time
	struct timeval tv;
	tv.tv_sec = delay; // three seconds
	tv.tv_usec = 0;
	setsockopt(_sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
#ifdef SO_RECVTIMEO
#endif
}

char*
Server::get_buffer(){
    return _buffer;
}

void
Server::set_buffer(char *ptr, int len = PROTO_LENG){
    _blen = len;
    memcpy(_buffer, ptr, _blen);
}

int
Server::Recv(){
    _slen = sizeof(SA);
    // memset(&_cliaddr, 0, sizeof(_cliaddr));
    return recvfrom(_sock, _buffer, PROTO_LENG_TMP, 0, (SA *)&_cliaddr, &_slen);
}

bool
Server::Send(ip_t ip){
    memset(&_cliaddr, 0, sizeof(_cliaddr));
    _cliaddr.sin_family = AF_INET;
    _cliaddr.sin_addr.s_addr = htonl(ip);
    // _cliaddr.sin_addr.s_addr = inet_addr(ip);
    _cliaddr.sin_port = htons(CLIENT_PORT);
    return Send();
}

bool
Server::Send(){
    if(-1 == sendto(_sock, _buffer, (size_t)_blen, 0, (SA *)&_cliaddr, _slen = sizeof(_cliaddr))){
        return false;
    }
    return true;
}

void
Server::servo(){
    Config* cnf = Config::theConfig(GEN_CONF_FILE);
    
    string serv_port_str = cnf->read("SERVER","PORT");
    int serv_port = atoi(serv_port_str.c_str());

    if(serv_port <=0){
        throw SocketException("Servo port wrong");
    }

    Listen(serv_port);

    Heart* _ht;
    try{
        _ht = Heart::theHeart();
    }
    catch(...){
        // throw HeartEngineFailed("Server::servo");
        LFatal("Server failed by heart!");
    }
    Alarm* _ala;
    try{
        _ala = Alarm::theAlarm();
    }
    catch(...){
        LFatal("Server failed by alarm!");
    }

    int rlen;
    ip_t ip;
    devid_t devid;
    
	while(1){
        rlen = Recv();
        
		if(-1 == rlen){
            LDebug( "Recv() error:" << strerror(errno) );
        }else{
            Proto pt(get_buffer(), ip = client_ip(), false);
            devid = pt.get_devid();
            
            LDebug("Received devid(" << rlen << "):" << devid << ", ip:"<< client_ip_str() << "::" <<  print_hex(_buffer,rlen) << " STATUS:" << pt.get_stat());

			if(rlen == PROTO_LENG){
                LDebug("authorization~");

                // when client reset, auth from db
                // otherwise, auth from heart hash

                bool passit = true;
                alarm_t last_reset;
                
                Proto* tmppro = _ht->get_proto(devid);

                if(NULL == tmppro){
                    // a new client device is connecting in ...
                    if(_ht->auth_db(devid)){
                        // update _auth value
                        passit = false;

                        // fetch last status before its offline
                        last_reset = _ht->get_last_alarm_db(devid);
                        pt.set_alarm(last_reset);
                    }                        
                }else{
                    last_reset = (tmppro->get_alarm() == CTRL_UNDEF)? CTRL_UNDEF: CTRL_DEFEN ;
                    
                    if( ! tmppro->is_auth()){
                        if(pt.is_reset()){
                            // when reset
                            if(_ht->auth_db(devid)){
                                passit = false;
                                pt.set_alarm(last_reset);
                            }
                        }
                    }else{
                        // this device client has been authed
                        passit = false;
                        if(pt.is_reset()){
                            pt.set_alarm(last_reset);
                        }
                    }
                }

                pt.set_auth(! passit);
                
                _ht->push_heart(pt);
                // _ht->dblog_record(&pt);
                
                if(passit) {
                    LDebug("Unauthed!!!!!!!!!!!!!!!!!");
                    continue;
                }else{
                    LDebug("Authed.................");
                }


                LDebug("IP:" << client_ip_str());
                
				if(ip == 0){
                    LDebug("IP is null!!");
                    continue;
                }

                LDebug("Received devid: " << ", alarm: "<<pt.get_alarm());
                
                if(pt.do_reply()){
                    if(pt.do_alarm()){
                        _ala->queue_alarm(devid, pt.get_alarm());
                    }
                    set_buffer(pt.get_buffer(), PROTO_LENG);
                    Send();
                    LDebug("Send to devid(" << rlen << "):" << devid << ", ip:"<< client_ip_str() << "::" <<  print_hex(_buffer,rlen) );
                }

			}
		}
	}
}

char*
Server::host_addr(){
	char hostname[MAXLEN];
	if(gethostname(tmpbuf, sizeof(tmpbuf)) == -1){
        tmpbuf[0] = '\0';
	}else{
		struct hostent *h;
		if((h = gethostbyname(tmpbuf)) == NULL){ // get address
            tmpbuf[0] = '\0';            
		}else{
            strncpy(tmpbuf, inet_ntoa(*((struct in_addr *)h->h_addr)), MAXLEN);
        }
	}
    return tmpbuf;
}

int
Server::host_port(){
    return (int)ntohs(_addr.sin_port);
}

ip_t
Server::client_ip(){
    return (ip_t)ntohl(_cliaddr.sin_addr.s_addr);
}

char*
Server::client_ip_str(){
    return inet_ntoa((struct in_addr)_cliaddr.sin_addr);
}

void
Server::Close(){
    close(_sock);
}
